#pragma once
#include <iostream>
#include <thread>
#include <unistd.h>
#include <vector>
#include <mutex>
#include <unordered_map>
#include <stdarg.h>
#include <cstdio>
#include <atomic>
#include "format.hpp"
#include "level.hpp"
#include "sink.hpp"
#include "util.hpp"
#include "LogMessage.hpp"
#include "looper.hpp"
#include "Buffer.hpp"



namespace my_log
{
    class Logger
    {
    public:
        using LogPtr = std::shared_ptr<Logger>;

        Logger(std::string logger_name, std::vector<my_log::Sink::SinkPtr> sinks, 
                my_log::Formatter::ptr fmt, my_log::LogLevel::level level)
            :_logger_name(logger_name),
            _sinks(sinks),
            _fmt(fmt),
            _limit_level(level)
        {}

        std::string &GetLoggername()
        {
            return _logger_name;
        }

        void DeBug(const std::string &file, size_t linesize, const std::string &fmt, ...)
        {
            if (my_log::LogLevel::level::DEBUG < _limit_level)
            {
                return;
            }
            va_list va;
            va_start(va, fmt);    //定位指针
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), va);    //此时res里面就是组织起来的消息
            if (ret < 0)
            {
                std::cout << "debug log error" << std::endl;
                return;
            }
            va_end(va);

            seriasize(file, linesize, res, my_log::LogLevel::level::DEBUG);
        }

        void Info(const std::string &file, size_t linesize, const std::string &fmt, ...)   //这个fmt是格式
        {
            if (my_log::LogLevel::level::INFO < _limit_level)
            {
                return;
            }

            va_list va;
            va_start(va, fmt);
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), va);
            if (ret < 0)
            {
                std::cout << "info log error" << std::endl;
                return;
            }

            va_end(va);
            seriasize(file, linesize, res, my_log::LogLevel::level::INFO);
        }

        void Warn(const std::string &file, size_t linesize, const std::string &fmt, ...)
        {
            if (my_log::LogLevel::level::WARN < _limit_level)
            {
                return;
            }

            va_list va;
            va_start(va, fmt);
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), va);
            if (ret < 0)
            {
                std::cout << "warn log error" << std::endl;
            }

            va_end(va);
            seriasize(file, linesize, res, my_log::LogLevel::level::WARN);
        }

        void Error(const std::string &file, size_t linesize, const std::string &fmt, ...)
        {
            if (my_log::LogLevel::level::ERROR < _limit_level)
            {
                return;
            }

            va_list va;
            va_start(va, fmt);
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), va);
            if (ret < 0)
            {
                std::cout << "error log error" << std::endl;
            }

            va_end(va);
            seriasize(file, linesize, res, my_log::LogLevel::level::ERROR);
        }
        void Fatal(const std::string &file, size_t linesize, const std::string &fmt, ...)
        {
            if (my_log::LogLevel::level::FATAL < _limit_level)
            {
                return;
            }

            va_list va;
            va_start(va, fmt);
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), va);
            if (ret < 0)
            {
                std::cout << "fatal log error" << std::endl;
            }

            va_end(va);
            seriasize(file, linesize, res, my_log::LogLevel::level::FATAL);
        }
        
    protected:
        void seriasize(const std::string &file, size_t linesize, char *res, my_log::LogLevel::level level)
        {
            //res是组织起来的消息， 就是payload
            std::string msg(res);
            my_log::Log_Msg logmsg(file, linesize, _logger_name,
                                    level, msg);
            
            
            //组织起来的就是logmsg， 然后将logmsg交给_fmt进行格式化
            std::string ret = _fmt->format(logmsg);     //得到的就是格式化后的信息, 对格式化后的信息进行落地，
                                                //使用落地器

                
            log((void *)ret.c_str(), ret.size());
        }   
        
        virtual void log(void * data, size_t len) = 0;

    protected:
        std::string _logger_name;
        std::vector<my_log::Sink::SinkPtr> _sinks;
        my_log::Formatter::ptr _fmt;
        my_log::LogLevel::level _limit_level;
        std::mutex _mutex;
    };


    class SyncLogger : public Logger
    {
    public:
        SyncLogger(std::string &logger_name, std::vector<my_log::Sink::SinkPtr> sinks, 
                    my_log::Formatter::ptr fmt, my_log::LogLevel::level level)
            :Logger(logger_name, sinks, fmt, level)
        {}

    private:
        void log(void *data, size_t len) override
        {
            std::unique_lock<std::mutex> lock(_mutex);
            if (_sinks.size() == 0) return;

            for (auto &sink : _sinks)
            {
                sink->log((void*)data, len);
            }
        }

    public:
        static void Test()
        {
            std::string logname = "rootlogger";
            my_log::Sink::SinkPtr ptr1 = my_log::SinkFactory<StdOutSink>::create();
            my_log::Sink::SinkPtr ptr2 = my_log::SinkFactory<FileSink>::create("./Log/sigfile"); 
            my_log::Sink::SinkPtr ptr3 = my_log::SinkFactory<RollSink>::create("./Log/rollfile-", 10000);
            std::vector<Sink::SinkPtr> sinks{ ptr1, ptr2, ptr3 };

            Formatter::ptr fmt = make_shared<Formatter>();


            SyncLogger Slog(logname, sinks, fmt, my_log::LogLevel::level::DEBUG);
            
            int cnt = 0;
            while (cnt < 50 * 10000)
            {
                cnt++;
                Slog.Info(__FILE__, __LINE__, ":%d", cnt);
            }
        }
    };

    class AsyncLogger : public Logger
    {
    public:
        AsyncLogger(std::string &logger_name, std::vector<my_log::Sink::SinkPtr> sinks,
                        my_log::Formatter::ptr fmt, my_log::LogLevel::level level, 
                        my_log::AsyncType looper_type = my_log::AsyncType::ASYNCTYPE_SAFE)
            :Logger(logger_name, sinks, fmt, level),
            _loop(std::bind(&AsyncLogger::rellog, this, std::placeholders::_1), _looper_type)
        {}

        void log(void *data, size_t len) override
        {
            _loop.push(data, len);
        }

        void rellog(Buffer *buf)
        {
            for (auto &sink : _sinks)
            {
                sink->log(buf->ReadPostion(), buf->ReadAbleSize());
            }
            buf->MoveReader(buf->ReadAbleSize());
        }

    private:
        my_log::AsyncLooper _loop;
        my_log::AsyncType _looper_type;

    public:

        static void Test()
        {
            std::string logname = "rootlogger";
            my_log::Sink::SinkPtr ptr1 = my_log::SinkFactory<StdOutSink>::create();
            my_log::Sink::SinkPtr ptr2 = my_log::SinkFactory<FileSink>::create("./Log/sigfile"); 
            my_log::Sink::SinkPtr ptr3 = my_log::SinkFactory<RollSink>::create("./Log/rollfile-", 10000);
            std::vector<Sink::SinkPtr> sinks{ ptr1, ptr2, ptr3 };

            Formatter::ptr fmt = make_shared<Formatter>();


            AsyncLogger aslog(logname, sinks, fmt, my_log::LogLevel::level::DEBUG);
            
            int cnt = 0;
            while (cnt < 5 * 10000)
            {
                cnt++;
                aslog.Info(__FILE__, __LINE__, ":%d", cnt);
            }
        }

    };


    enum class LoggerType
    {
        SYNCLOGGER,
        ASYNCLOGGER
    };

    class LoggerBuilder
    {
    public:
        LoggerBuilder()
            :_logger_type(my_log::LoggerType::SYNCLOGGER),
            _looper_type(my_log::AsyncType::ASYNCTYPE_SAFE),
            _limit_level(my_log::LogLevel::level::DEBUG)
        {}

        void SetLoggerType(my_log::LoggerType loggertype)
        {
            _logger_type = loggertype;
        }

        void SetLooperType(my_log::AsyncType looptype)
        {
            _looper_type = looptype;
        }

        void SetLimitLevel(my_log::LogLevel::level level)
        {
            _limit_level = level;
        }

        void SetLoggerName(const std::string &loggername)
        {
            _logger_name = loggername;
        }

        void SetFormatter(my_log::Formatter::ptr fmt)
        {
            _fmt = move(fmt);   //释放原来的资源， 管理后来的资源
        }

        template<class SinkType, class ...Args>
        void SetSink(Args &&...args)
        {
            Sink::SinkPtr ptr = my_log::SinkFactory<SinkType>::create(std::forward<Args>(args)...);
            _sinks.push_back(ptr);
        }

        virtual Logger::LogPtr build() = 0;

    protected:
        my_log::LoggerType _logger_type;
        my_log::AsyncType _looper_type;
        std::string _logger_name;
        std::vector<my_log::Sink::SinkPtr> _sinks;
        my_log::Formatter::ptr _fmt;
        my_log::LogLevel::level _limit_level;
        std::mutex _mutex;
    };

    class LocalLoggerBuilder : public LoggerBuilder
    {
    public:
        LocalLoggerBuilder()
        {}

        Logger::LogPtr build()
        {
            assert(!_logger_name.empty());

            if (_fmt.get() == nullptr)
            {
                _fmt = std::make_shared<Formatter>();
            }

            if (_sinks.size() == 0)
            {
                SetSink<StdOutSink>();
            }

            if (_logger_type == my_log::LoggerType::ASYNCLOGGER)
            {
                return std::make_shared<my_log::AsyncLogger>(_logger_name, _sinks, _fmt, _limit_level, _looper_type);
            }
            else 
            {
                return std::make_shared<my_log::SyncLogger>(_logger_name, _sinks, _fmt, _limit_level);
            }

        }
    };

    class LoggerMessager
    {
    public:
        static LoggerMessager &GetInstance()
        {
            static LoggerMessager eton;
            return eton;
        }

        void addLogger(Logger::LogPtr logger)
        {
            if (hasLogger(logger->GetLoggername())) return;
            std::unique_lock<std::mutex> lock(_mutex);
            
            _loggers.insert({logger->GetLoggername(), logger});
        }

        bool hasLogger(const string &name)
        {
            std::unique_lock<std::mutex> lock(_mutex);   //加锁
            auto it = _loggers.find(name);
            if (it == _loggers.end())
            {
                return false;
            }
            return true;
        }

        Logger::LogPtr GetLogger(const std::string &name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _loggers.find(name);
            if (it == _loggers.end()) 
            {
                return Logger::LogPtr();
            }
            return it->second;
        }

        Logger::LogPtr rootlogger()
        {
            return _root_logger;
        }

    private:
        LoggerMessager()
        {
            LoggerBuilder *lb = new LocalLoggerBuilder();
            lb->SetLoggerName("root_logger");
            lb->SetLoggerType(my_log::LoggerType::ASYNCLOGGER);
            _root_logger = lb->build();
            _loggers.insert({"root_logger", _root_logger});
        }

    private:
        std::mutex _mutex;
        Logger::LogPtr _root_logger;
        std::unordered_map<std::string, Logger::LogPtr> _loggers;
    };

    class GlobalLoggerBuilder : public LoggerBuilder
    {
        Logger::LogPtr build() override
        {
            assert(!_logger_name.empty());    //必须要有日志器名称
            if (_fmt.get() == nullptr)
            {
                _fmt = std::make_shared<Formatter>();
            }   
            
            if (_sinks.size() == 0)
            {
                SetSink<StdOutSink>();
            }

            Logger::LogPtr ptr;

            if (_logger_type == my_log::LoggerType::ASYNCLOGGER)
            {
                ptr = std::make_shared<my_log::AsyncLogger>(_logger_name, _sinks, _fmt, _limit_level, _looper_type);
            }
            else
            {
                ptr = std::make_shared<my_log::SyncLogger>(_logger_name, _sinks, _fmt, _limit_level);
            }
            LoggerMessager::GetInstance().addLogger(ptr);
            return ptr;
        }
    };

}
